#include "diskbittable.h"
#include "bithashtablehandle.h"
#include <errno.h>
#include "config.h"

DiskBitTable::DiskBitTable(std::string path)
    : m_path(path)
    , m_tableSize(TABLE_SIZE)
    , m_addr(NULL)
{
}

DiskBitTable::~DiskBitTable()
{
    if (m_addr != NULL) {
        munmap(m_addr, m_tableSize);
        close(m_tablefp);
    }

    if (m_hashHandle != NULL)
        delete m_hashHandle;
}

bool DiskBitTable::init()
{
    if (m_hashHandle != NULL)
        return false;

    if (m_path == "")
        return false;

    struct stat s;
    int ret = stat(m_path.c_str(), &s);
    if (ret == -1) {
        if (errno == ENOENT) {
            m_tablefp = open(m_path.c_str(), O_CREAT|O_RDWR, 00666);
            if (m_tablefp == -1) {
                perror("Create & open file error");
                return false;
            }

            off_t end = lseek(m_tablefp, m_tableSize-1, SEEK_SET);
            write(m_tablefp, "\0", 1);

            if (end == -1) {
                perror("Mmap file error");
                return false;
            }

            m_addr = (unsigned char *)mmap(NULL, m_tableSize, PROT_READ|PROT_WRITE, MAP_SHARED, m_tablefp, 0);
            memset(m_addr, 0, m_tableSize);
            if (m_addr == NULL) {
                perror("Mmap file error");
                return false;
            }
            return true;
        } else {
            perror("Find file error");
            return false;
        }
    }

    if (!(s.st_mode & S_IFREG)) {
        printf("File exists but not a regular file.");
        return false;
    }

    if (s.st_size < m_tableSize) {
        printf("File maybe broken.");
        return false;
    }

    m_tablefp = open(m_path.c_str(), O_RDWR, 00666);
    if (m_tablefp == -1) {
        perror("Open file error");
        return false;
    }

    m_addr = (unsigned char *)mmap(NULL, m_tableSize, PROT_READ|PROT_WRITE, MAP_SHARED, m_tablefp, 0);
    if (m_addr == NULL) {
        perror("Mmap file error");
        return false;
    }

    if (m_hashHandle == NULL)
        m_hashHandle = new BitHashTableHandle(this);

    return true;
}

bool DiskBitTable::getBitValue(unsigned long pos)
{
    unsigned char* p = m_addr;
    p += pos/8;
    unsigned char mask = 0x01 << pos%8;
    return (((*p) & mask)>>pos%8);
}

void DiskBitTable::setBitValue(unsigned long pos)
{
    unsigned char* p = m_addr;
    p += pos/8;
    unsigned char mask = 0x01 << pos%8;
    *p |= mask;
}

void DiskBitTable::clearBitValue(unsigned long pos)
{
    unsigned char* p = m_addr;
    p += pos/8;
    unsigned char mask = !(0x01 << pos%8);
    *p &= mask;
}

void DiskBitTable::clear()
{
    memset(m_addr, 0, m_tableSize);
}

void DiskBitTable::tsync(int flags)
{
    msync(m_addr, m_tableSize, flags);
}

